﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ImagePro
{
    public class ColorClustering
    {
        public ColorClustering(List<Color> colors, int category = 5, int limitTimes = 100)
        {
            Colors = colors;
            Category = category;
            LimitTimes = limitTimes;
            ColorClasses = new List<ColorClass>();
            ColorClasses.AddRange(colors.GroupBy(c => c).Select(r => new { color = r.Key, count = r.Count() }).OrderByDescending(x => x.count).Take(category).Select(r => new ColorClass(r.color)));
        }

        public delegate void Progress(int p);
        public List<ColorClass> Start(Progress pro)
        {
            pro(0);
            int times = 0;
            do
            {
                foreach (ColorClass cla in ColorClasses)
                {
                    cla.Members.Clear();
                }
                foreach (Color color in Colors)
                {
                    int minIndex = 0, minVal = ColorSub(ColorClasses[0].Center, color);
                    for (int i = 1; i < ColorClasses.Count; i++)
                    {
                        int p = ColorSub(ColorClasses[i].Center, color);
                        if (p < minVal)
                        {
                            minIndex = i;
                            minVal = p;
                        }
                    }
                    ColorClasses[minIndex].Members.Add(color);
                }
                pro(100 * times / LimitTimes);
                times++;
            } while (ColorClasses.Any(c => c.UpdateCenter()) && times < LimitTimes);
            pro(100);
            return ColorClasses;
        }

        public static int ColorSub(Color c1, Color c2)
        {
            return (int)Math.Sqrt(Math.Pow(c1.R - c2.R, 2) + Math.Pow(c1.G - c2.G, 2) + Math.Pow(c1.B - c2.B, 2));
        }

        public List<ColorClass> ColorClasses { get; }
        public List<Color> Colors { get; }
        public int Category { get; }
        public int LimitTimes { get; }
    }

    public class ColorClass
    {
        public ColorClass(Color center)
        {
            Center = center;
            Members = new List<Color>();
        }

        public bool UpdateCenter()
        {
            Color c = Center;
            if (Members.Count == 0)
            {
                return false;
            }
            Center = Color.FromArgb((int)Members.Average(m => m.R), (int)Members.Average(m => m.G), (int)Members.Average(m => m.B));
            return c != Center;
        }

        public List<Color> Members { get; set; }
        public Color Center { get; set; }
    }
}
